Router = require "koa-router"
path = require "path"
monk = require "monk"
axios = require "axios"
fs = require "fs"
fspromise = require "fs/promises"
envfile = require "envfile"
{ customAlphabet } = require "nanoid/async"
db = require "../db"
utils = require "../utils"
crypto = require "crypto"
_ = require "lodash"
dayjs = require "dayjs"
router = new Router prefix: "/admin"
Bot = require "../bot"
utils = require "../utils"

os = require('os')
startTime = dayjs().format('YYYY-MM-DD HH:mm:ss')

module.exports =
  checkAdmin: (ctx, next) ->
    ctx.state.layout = "layouts/admin_layout"
    unless ctx.session.admin
      return ctx.redirect "/admin/login"
    await next()
  login: (ctx) ->
    ctx.state.layout = "layouts/admin_layout"
    await ctx.render "admin/login"

  reboot: (ctx) ->
    {hard} = ctx.request.body
    exec = require("child_process").exec
    # await Bot.get().stop()
    # await Bot.get().start()
    setTimeout(
      ->
        exec "pm2 restart " + "bot-" + process.env.APPID
    ,
      50
    )
    ctx.body = "重启成功, 等待10s左右再访问后台页面, 请勿重复执行.否则重新等待."

  # 跳转协助管理
  manage: (ctx) ->
    clientid = ctx.query.clientid
    client = await db.CLIENT.findOne clientid
    ctx.session.openid = client.openid

    await ctx.redirect "/client/home"

  manageContact: (ctx) ->
    contactid = ctx.query.contactid
    client = await db.CLIENT.findOne contactid: contactid
    ctx.session.openid = client.openid
    await ctx.redirect "/client/home"

  redeemsPage: (ctx) ->
    await ctx.render "admin/redeems"
  redeemNewPage: (ctx) ->
    await ctx.render "admin/redeem_new"

  getRedeems: (ctx) ->
    redeems = await db.REDEEM.find {}, sort: _id: -1
    ctx.body = status: true, redeems: redeems

  redeemGenerate: (ctx) ->
    { num, title, days } = ctx.request.body
    nanoid = customAlphabet "0123456789abcdefghijklmnopqrstuvwxyz", 10
    for i in [1..num]
      code = await nanoid()
      await db.REDEEM.insert
        title: title
        code: code
        days: days
    ctx.body = status: true

  redeemExport: (ctx) ->
    redeems = await db.REDEEM.find {}, sort: _id: -1
    csv = "\uFEFF"
    csv += "code, 描述,兑换天数, 兑换时间,  群聊\n"
    for redeem in redeems
      line =
        [
          redeem.code
          redeem.title
          redeem.days or 30
          if redeem.redeemTime then dayjs(redeem.redeemTime).format "YYYY-MM-DD HH:mm:ss" else ""
          if redeem.topic then redeem.topic else ""
        ].join(",") + "\n"
      csv += line
    ctx.type = "text/csv; charset=utf8"
    ctx.attachment "redeems.csv"
    ctx.body = csv

  getRedeem: (ctx) ->
    redeems = await db.REDEEM.find()
    ctx.body = status: true, redeems: redeems

  intro: (ctx) ->
    await ctx.render "admin/intro"
  signin: (ctx) ->
    { password } = ctx.request.body
    { PASSWORD, APPID } = process.env
    encrypted = password
    # cancel encrypt for self deploy
    # encrypted = crypto
    #   .createHash "md5"
    #   .update password + APPID
    #   .digest "hex"
    #   .toUpperCase()

    if encrypted is PASSWORD
      ctx.session.admin = true
      return (ctx.body = status: true)

    ctx.body = status: false

  home: (ctx) ->
    ip = ctx.ip

    setting = await db.getMainSetting()

    await ctx.render "admin/home", setting: _.pick(setting, 'keyrooms'), sensitive: utils.sensitive, unescape: unescape, ip:ip, startTime: startTime

  roomsPage: (ctx) ->
    await ctx.render "admin/rooms"

  clientsPage: (ctx) ->
    await ctx.render "admin/clients"

  ordersPage: (ctx) ->
    await ctx.render "admin/orders"

  sendsPage: (ctx) ->
    await ctx.render "admin/sends"

  roomPage: (ctx) ->
    room = await db.ROOM.findOne id: ctx.params.roomid
    pointRule = await db.POINT_RULE.findOne roomid: room.id
    ctx.state.toml = require "@iarna/toml"
    await ctx.render "admin/room", room: room, pointRule: pointRule

  contactPage: (ctx) ->
    contact = await db.CONTACT.findOne id: ctx.params.contactid
    rooms = await db.ROOM.find inviterid: contact.id
    client = await db.CLIENT.findOne contactid: contact.id
    ctx.state.toml = require "@iarna/toml"
    await ctx.render "admin/contact", contact: contact, rooms: rooms, client: client

  settingPage: (ctx) ->
    setting = await db.getMainSetting()
    await ctx.render "admin/setting", setting: setting, sensitive: utils.sensitive

  getStat: (ctx) ->
    totalCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: true
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    todayCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: true
          _id: utils.mongoToday()
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    yestodayCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: true
          _id: utils.mongoYestoday()
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    redCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: false
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    todayRedCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: false
          _id: utils.mongoToday()
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    yestodayRedCharge =
      await db.ORDER.aggregate [
        $match:
          completed: true
          room:
            $exists: false
          _id: utils.mongoYestoday()
      ,
        $group:
          _id: "charge"
          sum:
            $sum: "$total_fee"
      ]

    ctx.body =
      status: true
      stat:
        totalCharge: totalCharge[0]
        todayCharge: todayCharge[0]
        yestodayCharge: yestodayCharge[0]
        redCharge: redCharge[0]
        todayRedCharge: todayRedCharge[0]
        yestodayRedCharge: yestodayRedCharge[0]

  getLoginQr: (ctx) ->
    ctx.body =
      status: true
      qrcode: Bot.get().qrcode

  getLoginStatus: (ctx) ->
    ctx.body =
      status: true
      onoff: Bot.get().isLoggedIn

  botLogout: (ctx) ->
    Bot.get().logout()
    ctx.body = status: true
  botInfo: (ctx) ->
    bot = await db.BOT.findOne id: Bot.getPayload().id
    # console.log Bot.get().currentUser
    currentUser =  Bot.get().currentUser
    if currentUser
      bot =
        _id: bot._id
        id: currentUser.id
        name: currentUser.name()
        # avatar: await currentUser.avatar()
    ctx.body =
      status: true
      bot: bot

  saveBot: (ctx) ->
    bot = await Bot.getPayload()
    db_bot = await db.BOT.findOneAndUpdate { id: bot.id }, $set: _.pick ctx.request.body, ["charge_month"]
    ctx.body = status: true

  botLogout: (ctx) ->
    Bot.get().logout()
    ctx.body = status: true

  getOrders: (ctx) ->
    # bot = Bot.getPayload()
    orders = await db.ORDER.find { completed: true }, sort: { _id: -1 }, limit: 200
    for order in orders
      order.user = await db.USER.findOne contactid: order.client.contactid
      order.datetime = dayjs(order._id.getTimestamp()).format "YYYY-MM-DD HH:mm"

    ctx.body = status: true, orders: orders

  getSends: (ctx) ->
    # bot = Bot.getPayload()
    sends =
      await db.SEND.find {},
        sort: _id: -1
        limit: 200
        fields: ["rooms", "clientid", "name", "mode", "weekdays", "daytime", "days", "month"]
    for send in sends
      send.user = await db.USER.findOne contactid: sends.clientid

    ctx.body = status: true, sends: sends

  getClients: (ctx) ->
    bot = Bot.getPayload()
    clients =
      await db.CLIENT.find {},
        sort:
          red_fee: -1
          _id: -1
        # limit: 1000
    for client in clients
      client.user = await db.USER.findOne contactid: client.contactid
      # client.rooms = await db.ROOM.find inviterid: client.contactid
      # for room in client.rooms
      #   room.membersLength = room.memberIdList.length
      #   delete room.memberIdList
      #   room.vip = room.expires and room.expires > new Date()
      #   room.expiresDate = dayjs(room.expires).format "YYYY-MM-DD"

    ctx.body =
      status: true
      clients: clients

  setClientRedFee: (ctx)->
    client = ctx.request.body
    await db.CLIENT.findOneAndUpdate
      _id: monk.id client._id
    ,
      $set:
        red_fee: client.balance*100
    ctx.body =
      status: true

  getRooms: (ctx) ->
    bot = Bot.getPayload()

    rooms =
      await db.ROOM.find {},
        sort:
          expires: -1
          _id: -1
        limit: 500
    for room in rooms
      room.membersLength = room.memberIdList.length
      delete room.memberIdList
      if room.inviterid
        room.inviter = await db.USER.findOne contactid: room.inviterid

      room.vip = room.expires > new Date()
      room.expiresDate = dayjs(room.expires).format "YYYY-MM-DD"
      # room.invite =
      #   await db.POINT_LOG.count
      #     roomid: room.id
      #     type: $ne: "sign"
      # room.sign =
      #   await db.POINT_LOG.count
      #     roomid: room.id
      #     type: "sign"

    ctx.body =
      status: true
      rooms: rooms

      # clientRooms =
    # 	await db.ROOM.find
    # 		expires:
    # 			$gt: new Date()
    # 	,
    # 		$sort: expires: 1

    # expiresrooms =
    # 	await db.ROOM.find
    # 		_id:
    # 			$nin: clientRooms.map (i) -> _._id
    # 	,
    # 		$sort: _id: -1

  syncRooms: (ctx) ->
    rooms = await Bot.get().Room.findAll()
    bot = Bot.getPayload()
    for room in rooms
      await db.ROOM.findOneAndUpdate
        botid: bot.id
        id: room.id
      ,
        $set: room.payload
      ,
        upsert: true

    ctx.body = status: true

  setRoomClient: (ctx)->
    room = await db.ROOM.findOneAndUpdate
        _id: monk.id(ctx.request.body._id)
      ,
        $set:
          inviterid: ctx.request.body.inviterid
    ctx.body = status: true

  getContacts: (ctx) ->
    bot = Bot.getPayload()
    contacts = await db.CONTACT.find { botid: bot.id }, limit: 200

    ctx.body = status: true, contacts: contacts

  syncContacts: (ctx) ->
    contacts = await Bot.get().Contact.findAll()
    bot = Bot.getPayload()

    for contact in contacts
      await db.CONTACT.findOneAndUpdate
        botid: bot.id
        id: contact.id
      ,
        $set: contact.payload
      ,
        upsert: true

      user =
        # headimgurl: contact.payload.avatar
        nickname: contact.payload.name
        contactid: contact.id
      id = await utils.getUserId user
      await db.USER.findOneAndUpdate { id: id }, { $set: user }, upsert: true

    ctx.body = status: true

  setRoomExpires: (ctx) ->
    { room } = ctx.request.body

    unless room.inputExpires
      return (ctx.body = status: false, msg: "inputExpires empty")
    await db.ROOM.findOneAndUpdate
      _id: monk.id room._id
    ,
      $set:
        expires: new Date room.inputExpires

    ctx.body = status: true

  saveSettingMain: (ctx) ->
    setting = ctx.request.body
    await db.SETTING.findOneAndUpdate { id: "main" }, { $set: setting }, upsert: true
    ctx.body = status: true

  livetest: (ctx) ->
    bots = await db.BOT.find()

    ctx.body = status: true, bots: bots, version: 2

  remonk: (ctx)->
    db.remonk()
    ctx.body = status: true

  saveSettingEnv: (ctx) ->
    env = ctx.request.body
    env = _.pickBy env, Boolean

    envpath = path.resolve __dirname, "../.env"
    parsed = envfile.parse fs.readFileSync(envpath).toString()
    for k of env
      # if k is "PASSWORD" and env[k]
      #   env[k] = crypto
      #     .createHash "md5"
      #     .update env[k] + process.env.APPID
      #     .digest "hex"
      #     .toUpperCase()
      # if k is "CSS"
      #   env[k] = escape env[k]
      parsed[k] = env[k]
      process.env[k] = env[k]
    fs.writeFileSync envpath, envfile.stringify parsed
    ctx.body = status: true

  uploadPfx: (ctx) ->
    { client } = ctx.state
    { file } = ctx.request.files
    try
      await fspromise.mkdir "./certs"
    catch e

    newPath = "/certs/#{process.env.APPID}_apiclient_cert.p12"
    await fspromise.rename file.path, "." + newPath

    envpath = path.resolve __dirname, "../.env"
    parsed = envfile.parse fs.readFileSync(envpath).toString()
    parsed["MCHPFX"] = "#{process.env.APPID}_apiclient_cert.p12"
    process.env["MCHPFX"] = "#{process.env.APPID}_apiclient_cert.p12"
    fs.writeFileSync envpath, envfile.stringify parsed
    ctx.body = status: true, path: newPath

  uploadQr: (ctx) ->
    { client } = ctx.state
    { file } = ctx.request.files
    try
      await fspromise.mkdir "./qrs"
    catch e
    newPath = "/qrs/#{process.env.APPID}_qr.png"
    await fspromise.rename file.path, "." + newPath
    stamp = new Date().getTime()
    envpath = path.resolve __dirname, "../.env"
    parsed = envfile.parse fs.readFileSync(envpath).toString()
    parsed["BOTQR"] = process.env.DOMAIN + "/qrs/#{process.env.APPID}_qr.png?" + stamp
    process.env["BOTQR"] = process.env.DOMAIN + "/qrs/#{process.env.APPID}_qr.png?" + stamp
    fs.writeFileSync envpath, envfile.stringify parsed
    ctx.body = status: true, path: newPath


  uploadImg: (ctx) ->
    { file } = ctx.request.files
    try
      await fspromise.mkdir "./upload"
    catch e

    newPath = "/upload/#{new Date().getTime()}.png"
    await fspromise.rename file.path, "." + newPath

    ctx.body = status: true, path: process.env.DOMAIN + newPath

  uploadVerify: (ctx) ->
    { client } = ctx.state
    { file } = ctx.request.files
    newPath = "/verify/" + file.name

    try
      await fspromise.mkdir "./verify"
    catch e

    await fspromise.rename file.path, "." + newPath

    envpath = path.resolve __dirname, "../.env"
    parsed = envfile.parse fs.readFileSync(envpath).toString()
    parsed["VERIFY"] = file.name
    process.env["VERIFY"] = file.name
    fs.writeFileSync envpath, envfile.stringify parsed
    ctx.body = status: true, path: file.name
